package com.sprite.framework.entity;

import com.sprite.framework.entity.idgenerator.SequenceGenerator;
import com.sprite.framework.entity.model.ModelEntity;
import com.sprite.framework.entity.model.ModelEntityUtil;
import com.sprite.framework.entity.model.ModelField;
import com.sprite.framework.entity.script.EntityDeleteScript;
import com.sprite.framework.entity.script.EntitySelectScript;
import com.sprite.framework.entity.script.EntityUpdateScript;
import com.sprite.utils.UtilBeans;
import com.sprite.utils.UtilCollection;
import com.sprite.utils.UtilMisc;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Delegator 的默认实现
 * @author Jack
 *
 */
public class GenericDelegator implements Delegator{
	private EntityInterceptor entityInterceptor;

	private EntityScriptExecutor scriptExecutor;

	public void setScriptExecutor(EntityScriptExecutor scriptExecutor) {
		this.scriptExecutor = scriptExecutor;
		// 添加默认的 sequence
		addGenerator(new SequenceGenerator(scriptExecutor, this));
	}

	public void setEntityInterceptor(EntityInterceptor entityInterceptor) {
		this.entityInterceptor = entityInterceptor;
	}

	@Override
	public <T> void store(String entityName, T entity) throws EntityException {
		if(entity == null)return;

		Map<String, Object> fields = null;
		if(Map.class.isInstance(entity)) {
			fields = UtilMisc.cast(entity);
		}else {
			fields = UtilBeans.toMap(entity);
		}
		
		ModelEntity modelEntity = ModelEntityUtil.getModelEntity(entityName);
		List<ModelField> keys = modelEntity.getKeys();
		EntityKey entityKey = new EntityKey();
		for(ModelField field :keys) {
			Object value = fields.get(field.getFieldName());
			if(value != null) {
				entityKey.put(field.getFieldName(), value);
			}
		}
		
		EntityScript script = null;
		if(!entityKey.isEmpty()) {
			EntityCondition whereCondition = EntityCondition.makeCondition(entityKey);
			if(this.count(entityName, whereCondition) >0) {// 已经存在
				if(entityInterceptor != null) {
					entityInterceptor.update(fields);
				}
				for(ModelField field :keys) {
					fields.remove(field.getFieldName());
				}
				
				fields.remove(FieldConstancts.CREATED_STAMP);
				if(modelEntity.hasField(FieldConstancts.UPDATED_STAMP)) {
					fields.put(FieldConstancts.UPDATED_STAMP, System.currentTimeMillis());
				}
				script = EntityScript.update(entityName, fields, whereCondition);
			}
		}else {
			setEntityId(entityName, entity, fields);
		}
		
		if(script == null) {
			if(modelEntity.hasField(FieldConstancts.CREATED_STAMP)) {
				fields.put(FieldConstancts.CREATED_STAMP, System.currentTimeMillis());
			}
			
			if(entityInterceptor != null) {
				entityInterceptor.save(fields);
			}
			
			script = EntityScript.insert(entityName, fields);
		}
		
		try{
			scriptExecutor.execute(script);

			//UtilBeans.setProperties(entity, ((EntityInsertScript)script).getGeneratedKey());
		}catch(Exception e){
			throw EntityException.getInstance(e);
		}
	}

	@Override
	public <T> void storeAll(String entityName, List<T> entities) throws EntityException {
		if(UtilCollection.isNotEmpty(entities)){
			try{
				for(T entity : entities){
					store(entityName, entity);
				}
			}catch(Exception e){
				throw EntityException.getInstance(e);
			}
		}
	}

	/**
	 * key is field type
	 */
	private volatile Map<String, IdGenerator> generators = new HashMap<>();

	public void addGenerator(IdGenerator generator){
		if(generators.put(generator.name(), generator) != null){
			throw new EntityException("IdGenerator["+ generator.name()+"] exists");
		}
	}

	public void addGenerator(List<IdGenerator> generators){
		if(UtilCollection.isEmpty(generators)){
			return ;
		}

		for(IdGenerator generator : generators){
			addGenerator(generator);
		}
	}

	private Map<String, IdGenerator> getGenerators(){
		return generators;
	}

	/**
	 * 设置实体主键
	 * @param entityName
	 * @param entity
	 * @param fields
	 */
	private void setEntityId(String entityName, Object entity, Map<String, Object> fields){
		if(entity == null) return ;

		List<ModelField> keys = ModelEntityUtil.getModelEntity(entityName).getKeys();
		if(UtilCollection.isEmpty(keys)) {
			return ;
		}

		for(ModelField keyField : keys){
			IdGenerator generator = getGenerators().get(keyField.getType());
			if(generator == null) {
				continue;
			}
			String idString = generator.nextId(entityName);
			UtilBeans.setProperty(entity, keyField.getFieldName(), idString);
			fields.put(keyField.getFieldName(), idString);
		}

	}

	/**
	 * 实体类是否逻辑删除
	 * @param entityName
	 * @return
	 * @throws EntityException
	 */
	private boolean isLogicalDeletable(String entityName) throws EntityException{
		return ModelEntityUtil.getModelEntity(entityName).isLogiclyDeletable();
	}

	@Override
	public <T> T findOne(String entityName, EntityCondition EntityCondition, Class<T> clazz)
			throws EntityException {
		try{
			EntitySelectScript script = new EntitySelectScript(entityName);
			script.selectAllField();
			script.setWhereCondition(EntityCondition);

			return onlyOne(scriptExecutor.query(script, clazz));

		}catch(Exception e){
			throw EntityException.getInstance(e);
		}
	}

	@Override
	public void update(String entityName, Map<String, Object> fieldsToSet,
			EntityCondition condition) throws EntityException {
		EntityUpdateScript updateScript = new EntityUpdateScript(entityName);
		updateScript.addFields(fieldsToSet);
		updateScript.setWhereCondition(condition);

		scriptExecutor.execute(updateScript);
	}


	@Override
	public void remove(String entityName) throws EntityException {
		try{
			EntityCondition condition = EntityCondition.any();

			if(isLogicalDeletable(entityName)){// 逻辑删除
				Map<String, Object> fieldsToSet = UtilMisc.toMap(FieldConstancts.DELETED, true);
				this.update(entityName, fieldsToSet, condition);
			}else{
				this.remove(entityName, condition);
			}

		}catch(Exception e){
			throw EntityException.getInstance(e);
		}
	}


	@Override
	public void remove(String entityName, EntityCondition EntityCondition) throws EntityException{
		if(isLogicalDeletable(entityName)){// 逻辑删除
			Map<String, Object> fieldsToSet = UtilMisc.toMap(FieldConstancts.DELETED, true);

			this.update(entityName, fieldsToSet, EntityCondition);
			return;
		}

		// 物理删除
		EntityDeleteScript script = new EntityDeleteScript(entityName);
		script.setWhereCondition(EntityCondition);
		scriptExecutor.execute(script);
	}

	@Override
	public <T> List<T> findList(String entityName, QueryArgs queryArgs, Class<T> clazz) throws EntityException {
		EntitySelectScript entityScript = new EntitySelectScript(entityName);
		entityScript.setWhereCondition(queryArgs.whereCondition);

		if(UtilCollection.isEmpty(queryArgs.queryFields)){// 设置查询所有字段
			entityScript.selectAllField();
		}else{
			for(String field : queryArgs.queryFields){
				entityScript.addSelectedField(field);
			}
		}


		if(UtilCollection.isNotEmpty(queryArgs.orderBy)){
			for(String field : queryArgs.orderBy){
				entityScript.addOrderByAsc(field);
			}
		}

		entityScript.setOptions(queryArgs.findOptions);

		return scriptExecutor.query(entityScript, clazz);
	}

	@Override
	public long count(String entityName, EntityCondition whereCondition)
			throws EntityException {
		EntitySelectScript entityScript = new EntitySelectScript(entityName);
		entityScript.setWhereCondition(whereCondition);
		return onlyOne(scriptExecutor.query(entityScript.countScript(), long.class));
		
	}

	@Override
	public long count(EntityScript script) throws EntityException {
		try{
			return onlyOne(scriptExecutor.query(script, long.class));
		}catch(Exception e){
			throw EntityException.getInstance(e);
		}
	}

	@Override
	public List<EntityObject> findList(EntityScript script) throws EntityException {
		return UtilMisc.cast(scriptExecutor.query(script, EntityObject.class));
	}

	@Override
	public EntityObject findOne(String entityName, QueryArgs queryArgs) throws EntityException {
		return onlyOne(findList(entityName, queryArgs));
	}

	@Override
	public <T> T findOne(String entityName, QueryArgs queryArgs, Class<T> clazz) throws EntityException {
		return onlyOne(findList(entityName, queryArgs, clazz));
	}

	@Override
	public <T> T findOne(EntityScript script, Class<T> clazz) throws EntityException {
		return UtilMisc.cast(onlyOne(scriptExecutor.query(script, clazz)));
	}

	@Override
	public <T> List<T> findList(EntityScript script, Class<T> clazz) throws EntityException {
		return UtilMisc.cast(scriptExecutor.query(script, clazz));
	}

	@Override
	public EntityObject findOne(String entityName, EntityCondition entityCondition) throws EntityException {
		return findOne(entityName, entityCondition, EntityObject.class);
	}

	@Override
	public List<EntityObject> findList(String entityName, QueryArgs queryArgs) throws EntityException {
		return findList(entityName, queryArgs, EntityObject.class);
	}

	@Override
	public <T> List<T> findList(String entityName, EntityCondition whereCondition, Class<T> clazz) throws EntityException {
		EntitySelectScript script = new EntitySelectScript(entityName);
		script.selectAllField();
		script.setWhereCondition(whereCondition);

		return findList(script, clazz);
	}

	@Override
	public List<EntityObject> findList(String entityName, EntityCondition whereCondition) throws EntityException {
		EntitySelectScript script = new EntitySelectScript(entityName);
		script.selectAllField();
		script.setWhereCondition(whereCondition);

		return findList(script);
	}

	@Override
	public EntityObject findOne(EntityScript script) throws EntityException {
		return findOne(script, EntityObject.class);
	}

	private <T> T onlyOne(List<T> list){
		if(UtilCollection.isEmpty(list)){
			return null;
		}

		if(list.size() > 1){
			throw new EntityException("un unique");
		}

		return UtilCollection.first(list);
	}
}
